// Intermediate code generator (3 address code)
#ifndef __IMM_H__
#define __IMM_H__

#include "all.h"
#include "Ast.h"
#include "STEClasses.h"
#include "ParserUtil.h"

class BasicBlock;

class ImmCode {
	public:
		enum Op {
			EXPR,                   /* x = y op z */
			IF,                     /* if x gotoL or ifFalse x goto */
			IF_REL,         /* if x relop y goto L */
			GOTO,                   /* unconditional jump */
			PARAM,
			CALL,
			ENTER,                  /* beginning of function */
			LEAVE,                  /* return value + leave */
			LABEL,
			INLINE                /* special purpose : LABEL */
		};
		// Initializer
		ImmCode(Op _t,OpNode::OpCode sub=OpNode::INVALID,
				void* r=0,void* a0=0,void* a1=0) {
			subCode = sub;
			op = _t;opnd[0] = r;opnd[1]=a0;opnd[2]=a1;
		}
		void print(ostream &os) {
			ExprNode** opd = (ExprNode**)opnd;
			switch(op) {
				case EXPR:
					switch(subCode) {
						case OpNode::PLUS:
						case OpNode::MINUS:
						case OpNode::MULT:
						case OpNode::DIV:
						case OpNode::MOD:
						case OpNode::BITAND:
						case OpNode::BITOR:
						case OpNode::BITXOR:
						case OpNode::SHL:
						case OpNode::SHR:
							os << '\t';
							os << opd[0]->addrName() << " = " << opd[1]->addrName();
							os << OpNode::opInfo[subCode].name_ << opd[2]->addrName();
							break;
						case OpNode::UMINUS:
						case OpNode::NOT:
						case OpNode::BITNOT:
							os << '\t';
							os << opd[0]->addrName() << " = ";
							os << OpNode::opInfo[subCode].name_ << opd[1]->addrName();
							break;
						case OpNode::ASSIGN:
							os << '\t';
							os << opd[0]->addrName() << " = " << opd[1]->addrName();
							break;
						case OpNode::PRINT:
							os << '\t';
							os << "print ";
							os << opd[1]->addrName();
							break;
					};
					break;
				case IF:
					{
						ImmCode *label = (ImmCode*)opnd[0];
						os << '\t';
						os << "if " << opd[1]->addrName() << " goto ";
						os << label->labelName();
					}
					break;
				case IF_REL:
					// 1-e1.addr , 2-e2.addr 0-goto
					{
						ImmCode *label = (ImmCode*)opnd[0];
						os << '\t';
						os << "if " << opd[1]->addrName() << " ";
						os << OpNode::opInfo[subCode].name_ << " ";
						os << opd[2]->addrName() << " goto ";
						os << label->labelName();
					}
					break;
				case GOTO:
					{
						os << '\t';
						ImmCode* label = (ImmCode*)opnd[0];
						os << "goto " << label->labelName();
					}
					break;
				case PARAM:
					os << '\t';
					os << "param " << opd[0]->addrName();

					break;
				case LEAVE:
					{
						os << '\t';
						SymTabEntry* ste = (SymTabEntry*)opnd[1];
						os << "leave " << ste->name();
						if (opd[0]) os << ", " << opd[0]->addrName();
					}
					break;
				case ENTER:
					{
						os << '\t';
						SymTabEntry* ste = (SymTabEntry*)opnd[0];
						os << "enter " << ste->name();
					}
					break;
				case CALL:
					{
						os << '\t';
						InvocationNode* iv = (InvocationNode*)opnd[0];
						SymTabEntry* ste = (SymTabEntry*)iv->symTabEntry();
						if (opd[1])
							os << opd[1]->addrName() << " = ";
                        /*FunctionEntry* fe = (FunctionEntry*) iv->symTabEntry();
                        if(!fe->hasRecursion() &&  fe->body()->stmts()->size() <= 5)
                        os << */

						os << "call " << ste->name();
					}
					break;
                case INLINE:
					{
						os << '\t';
						InvocationNode* iv = (InvocationNode*)opnd[0];
						if (opd[1])
							os << opd[1]->addrName() << " = ";
                        if (opd[2])
                            os << opd[2]->addrName();

						//os << "call " << ste->name();
					}
					break;
				case LABEL:
					unsigned int id = (long)opnd[0];
					string *name = (string*)opnd[1];
					if (name && name->length())
						os << *name << ":";
					else
						os << "L" << id << ":";
					break;
			};
		}
		string labelName() {
			if (op != LABEL) return "";
			unsigned int id = (long)opnd[0];
			string *name = (string*)opnd[1];
			if (name && name->length())
				return *name;
			else {
				ostringstream os;
				os << "L" << id;
				return os.str();
			}
		}
		Op getType(){
			return op;
		}
		//#ifdef TEST_CODEGEN
		Op get_op() {
			return op;
		}

		void reverse_subcode() {
			switch (subCode) {
				case OpNode::EQ:
					subCode = OpNode::NE;
					break;
				case OpNode::NE:
					subCode = OpNode::EQ;
					break;
				case OpNode::GT:
					subCode = OpNode::LE;
					break;
				case OpNode::GE:
					subCode = OpNode::LT;
					break;
				case OpNode::LE:
					subCode = OpNode::GT;
					break;
				case OpNode::LT:
					subCode = OpNode::GE;
					break;
				default:
					cout<<"Cannot negate the subcode()";
					break;
			}
		}
		OpNode::OpCode get_subcode() {
			return subCode;
		}

		void** get_opnd() {
			return opnd;
		}
		//#endif
	protected:
		Op op;
		OpNode::OpCode subCode; /* for EXPR,EXPR_U */
		void* opnd[3];  /* 3 operands */
};

class ImmCodes {
	public:
		static int labelCounter;
		ImmCodes() {}
		virtual ~ImmCodes() {}
		static ImmCode* newLabel(string name="") {
			ImmCode* code= new ImmCode(ImmCode::LABEL,OpNode::INVALID,
					(void*)labelCounter++,
					(void*)new string(name));
			return code;
		}
		void add(ImmCode::Op t,void* rslt=NULL, void* arg1=NULL,void* arg2=NULL,
				OpNode::OpCode sub=OpNode::INVALID) {
			m_codes.push_back(new ImmCode(t,sub,rslt,arg1,arg2));
		}
		void add(ImmCodes* code) {
			if (code==NULL) return;
			vector <ImmCode*>::iterator it = code->m_codes.begin();
			for(;it!=code->m_codes.end();it++) {
				m_codes.push_back((*it));
			}
		}
		void add(ImmCode* cd) {
			if (cd)
				m_codes.push_back(cd);
		}
		int count() {return m_codes.size();}
		vector <BasicBlock*>* divideBasicBlocks();
		void print(ostream &os) {
			vector <ImmCode*>::iterator it = m_codes.begin();
			for(;it!=m_codes.end();it++) {
				(*it)->print(os);
				os << endl;
			}
		}
		//#ifdef TEST_CODEGEN
		/* returns pointer to 3addr code */
		vector<ImmCode*>* get_code(){
			return &m_codes;
		}
		//#endif
	protected:
		vector <ImmCode*> m_codes;
	protected:
};



class BasicBlock: public ImmCodes {
	public:
		static int blockCounter;
		BasicBlock() {}

		BasicBlock(string inlabel){
			label = inlabel;
		}

		//Functions and Variables for Data Flow Analysis
		vector<string> processedLabels;
		vector<int> addedStatements;
		vector<ImmCode*> expressionList;
		BasicBlock* iterativeDataFlowAnalysis();
		BasicBlock* liveVariables(bool*);
		BasicBlock* reachingDefinitions(bool*);
		BasicBlock* availableExpressions(bool*);
		BasicBlock* arithmeticIdentities(bool*);
		bool isProcessed(string);
		int isStatementAdded(int);
		bool checkVariableUseInNextBlocks(string,string);
		bool checkVariableUseBeforeDef(string,string);
		//---------------------------------------------

		void addNextBlock(string nextBlockLabel){
			if (nextBlockLabel == "S0")
				return;
			vector <string>::iterator it = nextBlocksLabels.begin();
			for(;it!=nextBlocksLabels.end(); it++)
				if ((*it) == nextBlockLabel)
					return;
			nextBlocksLabels.push_back(nextBlockLabel);

		}

		void add(ImmCode* cd) {
			if (cd->getType() == ImmCode::LABEL && this->count() == 0)
				setLabel(cd->labelName());
			((ImmCodes*)this)->add(cd);
		}

		void setLabel(string inlabel){

			label = inlabel;
		}

		string getLabel(){
			return label;
		}

		vector<string>* getNextBlocks();

		void setLabelNode(ImmCode* labelNode){
			_labelNode = labelNode;
		}
		ImmCode* getLabelNode(){
			return _labelNode;
		}


        BasicBlock* iterativeOptimize(bool* updated5) {
		bool updated = false, updated2 = false;
		bool updated3 = false, updated4 = false;

		BasicBlock* temp = this;
		int i = 0;

		do {
			i++;
			updated = false;
			updated2 = false;

			temp = temp->staticEvaluation(&updated);
			temp = temp->constantPropogation(&updated2);
		} while (updated || updated2);

		*updated5 = i > 1;

		return temp;
	}



		BasicBlock* constantPropogation(bool* updated=false);

		BasicBlock* staticEvaluation(bool* updated = false){
			vector <ImmCode*>::iterator it = m_codes.begin();
			BasicBlock* seBB = new BasicBlock(label);
			for(;it!=m_codes.end();it++) {
				if ((*it)->getType() == ImmCode::EXPR)
					switch((*it)->get_subcode()) {
						case OpNode::PLUS:
							/*
							   case OpNode::EQ:
							   case OpNode::NE:
							   case OpNode::GT:
							   case OpNode::LT:
							   case OpNode::GE:
							   case OpNode::LE:
							   case OpNode::AND:
							   case OpNode::OR:
							   case OpNode::BITOR:
							   case OpNode::BITXOR:

							   */{
													ExprNode** opd = (ExprNode**)((*it)->get_opnd());
													if (opd[1]->exprNodeType() == ExprNode::VALUE_NODE && opd[2]->exprNodeType() == ExprNode::VALUE_NODE ){
														ValueNode* v;
														if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
															v = new ValueNode(new Value(opd[1]->value()->ival() + opd[2]->value()->ival(), Type::INT));
															*updated = true;
														}
														else if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isFloat(opd[2]->value()->type()->tag())){
															v = new ValueNode(new Value(opd[1]->value()->ival() + opd[2]->value()->dval()));
															*updated = true;
														}
														else if (Type::isFloat(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
															v = new ValueNode(new Value(opd[1]->value()->dval() + opd[2]->value()->ival()));
															*updated = true;
														}
														else if (Type::isFloat(opd[1]->value()->type()->tag()) && Type::isFloat(opd[2]->value()->type()->tag())){
															v = new ValueNode(new Value(opd[1]->value()->dval() + opd[2]->value()->dval()));
															*updated = true;
														}
														seBB->add(new ImmCode((*it)->getType(), OpNode::ASSIGN, opd[0], v  ));
													}
													else
														seBB->add(*it);
													break;
												}
						case OpNode::MINUS:{
											   ExprNode** opd = (ExprNode**)((*it)->get_opnd());
											   if (opd[1]->exprNodeType() == ExprNode::VALUE_NODE && opd[2]->exprNodeType() == ExprNode::VALUE_NODE ){
												   ValueNode* v;
												   if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
													   v = new ValueNode(new Value(opd[1]->value()->ival() - opd[2]->value()->ival(), Type::INT));
													   *updated = true;
												   }
												   else if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isFloat(opd[2]->value()->type()->tag())){
													   v = new ValueNode(new Value(opd[1]->value()->ival() - opd[2]->value()->dval()));
													   *updated = true;
												   }
												   else if (Type::isFloat(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
													   v = new ValueNode(new Value(opd[1]->value()->dval() - opd[2]->value()->ival()));
													   *updated = true;
												   }
												   else if (Type::isFloat(opd[1]->value()->type()->tag()) && Type::isFloat(opd[2]->value()->type()->tag())){
													   v = new ValueNode(new Value(opd[1]->value()->dval() - opd[2]->value()->dval()));
													   *updated = true;
												   }
												   seBB->add(new ImmCode((*it)->getType(), OpNode::ASSIGN, opd[0], v  ));
											   }
											   else
												   seBB->add(*it);
											   break;
										   }
						case OpNode::MULT:{
											  ExprNode** opd = (ExprNode**)((*it)->get_opnd());
											  if (opd[1]->exprNodeType() == ExprNode::VALUE_NODE && opd[2]->exprNodeType() == ExprNode::VALUE_NODE ){
												  ValueNode* v;
												  if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
													  v = new ValueNode(new Value(opd[1]->value()->ival() * opd[2]->value()->ival(), Type::INT));
													  *updated = true;
												  }
												  else if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isFloat(opd[2]->value()->type()->tag())){
													  v = new ValueNode(new Value(opd[1]->value()->ival() * opd[2]->value()->dval()));
													  *updated = true;
												  }
												  else if (Type::isFloat(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
													  v = new ValueNode(new Value(opd[1]->value()->dval() * opd[2]->value()->ival()));
													  *updated = true;
												  }
												  else if (Type::isFloat(opd[1]->value()->type()->tag()) && Type::isFloat(opd[2]->value()->type()->tag())){
													  v = new ValueNode(new Value(opd[1]->value()->dval() * opd[2]->value()->dval()));
													  *updated = true;
												  }
												  seBB->add(new ImmCode((*it)->getType(), OpNode::ASSIGN, opd[0], v  ));
											  }
											  else
												  seBB->add(*it);
											  break;
										  }
						case OpNode::DIV:{
											 ExprNode** opd = (ExprNode**)((*it)->get_opnd());
											 if (opd[1]->exprNodeType() == ExprNode::VALUE_NODE && opd[2]->exprNodeType() == ExprNode::VALUE_NODE ){
												 ValueNode* v;
												 if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
													 v = new ValueNode(new Value(opd[1]->value()->ival() / opd[2]->value()->ival(), Type::INT));
													 *updated = true;
												 }
												 else if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isFloat(opd[2]->value()->type()->tag())){
													 v = new ValueNode(new Value(opd[1]->value()->ival() / opd[2]->value()->dval()));
													 *updated = true;
												 }
												 else if (Type::isFloat(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
													 v = new ValueNode(new Value(opd[1]->value()->dval() / opd[2]->value()->ival()));
													 *updated = true;
												 }
												 else if (Type::isFloat(opd[1]->value()->type()->tag()) && Type::isFloat(opd[2]->value()->type()->tag())){
													 v = new ValueNode(new Value(opd[1]->value()->dval() / opd[2]->value()->dval()));
													 *updated = true;
												 }
												 seBB->add(new ImmCode((*it)->getType(), OpNode::ASSIGN, opd[0], v  ));
											 }
											 else
												 seBB->add(*it);
											 break;
										 }

						case OpNode::MOD:{
											 ExprNode** opd = (ExprNode**)((*it)->get_opnd());
											 if (opd[1]->exprNodeType() == ExprNode::VALUE_NODE && opd[2]->exprNodeType() == ExprNode::VALUE_NODE ){
												 ValueNode* v;
												 if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
													 v = new ValueNode(new Value(opd[1]->value()->ival() % opd[2]->value()->ival(), Type::INT));
													 *updated = true;
													 seBB->add(new ImmCode((*it)->getType(), OpNode::ASSIGN, opd[0], v  ));
												 }
												 else
													 //for floating point modular?
													 seBB->add(*it);
											 }
											 else
												 seBB->add(*it);
											 break;
										 }

						case OpNode::SHL:{
											 ExprNode** opd = (ExprNode**)((*it)->get_opnd());
											 if (opd[1]->exprNodeType() == ExprNode::VALUE_NODE && opd[2]->exprNodeType() == ExprNode::VALUE_NODE ){
												 ValueNode* v;
												 if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
													 v = new ValueNode(new Value(opd[1]->value()->ival() << opd[2]->value()->ival(), Type::INT));
													 *updated = true;
													 seBB->add(new ImmCode((*it)->getType(), OpNode::ASSIGN, opd[0], v  ));
												 }
												 else
													 //for floating point modular?
													 seBB->add(*it);
											 }
											 else
												 seBB->add(*it);
											 break;
										 }

						case OpNode::SHR:{
									 ExprNode** opd = (ExprNode**)((*it)->get_opnd());
									 if (opd[1]->exprNodeType() == ExprNode::VALUE_NODE && opd[2]->exprNodeType() == ExprNode::VALUE_NODE ){
										 ValueNode* v;
										 if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
											 v = new ValueNode(new Value(opd[1]->value()->ival() >> opd[2]->value()->ival(), Type::INT));
											 *updated = true;
											 seBB->add(new ImmCode((*it)->getType(), OpNode::ASSIGN, opd[0], v  ));
										 }
										 else
											 //for floating point modular?
											 seBB->add(*it);
									 }
									 else
										 seBB->add(*it);
									 break;
								 }

						case OpNode::BITAND:{
									 ExprNode** opd = (ExprNode**)((*it)->get_opnd());
									 if (opd[1]->exprNodeType() == ExprNode::VALUE_NODE && opd[2]->exprNodeType() == ExprNode::VALUE_NODE ){
										 ValueNode* v;
										 if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
											 v = new ValueNode(new Value(opd[1]->value()->ival() & opd[2]->value()->ival(), Type::INT));
											 *updated = true;
											 seBB->add(new ImmCode((*it)->getType(), OpNode::ASSIGN, opd[0], v  ));
										 }
										 else
											 //for floating point modular?
											 seBB->add(*it);
									 }
									 else
										 seBB->add(*it);
									 break;
								 }
						case OpNode::BITOR:{
									 ExprNode** opd = (ExprNode**)((*it)->get_opnd());
									 if (opd[1]->exprNodeType() == ExprNode::VALUE_NODE && opd[2]->exprNodeType() == ExprNode::VALUE_NODE ){
										 ValueNode* v;
										 if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
											 v = new ValueNode(new Value(opd[1]->value()->ival() | opd[2]->value()->ival(), Type::INT));
											 *updated = true;
											 seBB->add(new ImmCode((*it)->getType(), OpNode::ASSIGN, opd[0], v  ));
										 }
										 else
											 //for floating point modular?
											 seBB->add(*it);
									 }
									 else
										 seBB->add(*it);
									 break;
								 }
						case OpNode::BITXOR:{
									 ExprNode** opd = (ExprNode**)((*it)->get_opnd());
									 if (opd[1]->exprNodeType() == ExprNode::VALUE_NODE && opd[2]->exprNodeType() == ExprNode::VALUE_NODE ){
										 ValueNode* v;
										 if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
											 v = new ValueNode(new Value(opd[1]->value()->ival() ^ opd[2]->value()->ival(), Type::INT));
											 *updated = true;
											 seBB->add(new ImmCode((*it)->getType(), OpNode::ASSIGN, opd[0], v  ));
										 }
										 else
											 //for floating point modular?
											 seBB->add(*it);
									 }
									 else
										 seBB->add(*it);
									 break;
								 }
						case OpNode::BITNOT:{
									 ExprNode** opd = (ExprNode**)((*it)->get_opnd());
									 if (opd[1]->exprNodeType() == ExprNode::VALUE_NODE){
										 ValueNode* v;
										 if (Type::isInt(opd[1]->value()->type()->tag())){
											 v = new ValueNode(new Value(~opd[1]->value()->ival(), Type::INT));
											 *updated = true;
											 seBB->add(new ImmCode((*it)->getType(), OpNode::ASSIGN, opd[0], v  ));
										 }
										 else
											 //for floating point modular?
											 seBB->add(*it);
									 }
									 else
										 seBB->add(*it);
									 break;
								 }

						default:
										 seBB->add(*it);
					}
				else if ((*it)->getType() == ImmCode::IF_REL){
					ExprNode** opd = (ExprNode**)((*it)->get_opnd());
					if (opd[1]->exprNodeType() == ExprNode::VALUE_NODE && opd[2]->exprNodeType() == ExprNode::VALUE_NODE ){
						if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
							ValueNode* v;
							bool cond;
							int val1 = opd[1]->value()->ival();
							int val2 = opd[2]->value()->ival();
							switch ((*it)->get_subcode()) {
								case OpNode::EQ:
									cond = (val1 == val2);
									break;
								case OpNode::NE:
									cond = (val1 != val2);
									break;
								case OpNode::GT:
									cond = (val1 > val2);
									break;
								case OpNode::GE:
									cond = (val1 >= val2);
									break;
								case OpNode::LE:
									cond = (val1 <= val2);
									break;
								case OpNode::LT:
									cond = (val1 < val2);
									break;
								default:
									cout<<"unknow subCode()";
									cond = false;
									break;
							}
							if (cond)
								seBB->add(new ImmCode(ImmCode::GOTO, OpNode::INVALID, ((*it)->get_opnd())[0]));
						}
						else if (Type::isInt(opd[1]->value()->type()->tag()) && Type::isFloat(opd[2]->value()->type()->tag())){
							ValueNode* v;
							bool cond;
							int val1 = opd[1]->value()->ival();
							double val2 = opd[2]->value()->dval();
							switch ((*it)->get_subcode()) {
								case OpNode::EQ:
									cond = (val1 == val2);
									break;
								case OpNode::NE:
									cond = (val1 != val2);
									break;
								case OpNode::GT:
									cond = (val1 > val2);
									break;
								case OpNode::GE:
									cond = (val1 >= val2);
									break;
								case OpNode::LE:
									cond = (val1 <= val2);
									break;
								case OpNode::LT:
									cond = (val1 < val2);
									break;
								default:
									cout<<"unknow subCode()";
									cond = false;
									break;
							}
							if (cond)
								seBB->add(new ImmCode(ImmCode::GOTO, OpNode::INVALID, ((*it)->get_opnd())[0]));
						}
						else if (Type::isFloat(opd[1]->value()->type()->tag()) && Type::isInt(opd[2]->value()->type()->tag())){
							ValueNode* v;
							bool cond;
							double val1 = opd[1]->value()->dval();
							int val2 = opd[2]->value()->ival();
							switch ((*it)->get_subcode()) {
								case OpNode::EQ:
									cond = (val1 == val2);
									break;
								case OpNode::NE:
									cond = (val1 != val2);
									break;
								case OpNode::GT:
									cond = (val1 > val2);
									break;
								case OpNode::GE:
									cond = (val1 >= val2);
									break;
								case OpNode::LE:
									cond = (val1 <= val2);
									break;
								case OpNode::LT:
									cond = (val1 < val2);
									break;
								default:
									cout<<"unknow subCode()";
									cond = false;
									break;
							}
							if (cond)
								seBB->add(new ImmCode(ImmCode::GOTO, OpNode::INVALID, ((*it)->get_opnd())[0]));
						}
						else if (Type::isFloat(opd[1]->value()->type()->tag()) && Type::isFloat(opd[2]->value()->type()->tag())){
							ValueNode* v;
							bool cond;
							double val1 = opd[1]->value()->dval();
							double val2 = opd[2]->value()->dval();
							switch ((*it)->get_subcode()) {
								case OpNode::EQ:
									cond = (val1 == val2);
									break;
								case OpNode::NE:
									cond = (val1 != val2);
									break;
								case OpNode::GT:
									cond = (val1 > val2);
									break;
								case OpNode::GE:
									cond = (val1 >= val2);
									break;
								case OpNode::LE:
									cond = (val1 <= val2);
									break;
								case OpNode::LT:
									cond = (val1 < val2);
									break;
								default:
									cout<<"unknow subCode()";
									cond = false;
									break;
							}
							if (cond)
								seBB->add(new ImmCode(ImmCode::GOTO, OpNode::INVALID, ((*it)->get_opnd())[0]));
						}
						else
							seBB->add(*it);

					}
					else
						seBB->add(*it);

				}
				else
					seBB->add(*it);

			}

			//seBB->print(cout);
			//seBB->constantPropogation();

			return seBB;
		}


	private:
		vector <BasicBlock*> nextBlocks;
		string label;
		vector <string> nextBlocksLabels;
		ImmCode* _labelNode;
};



class BasicBlockManager {
	private:
		BasicBlockManager(){};
		static BasicBlockManager* _manager;
		vector <BasicBlock*> allBasicBlocks;
		BasicBlockManager(BasicBlockManager const &){};                 // Prevent copy-construction
		BasicBlockManager& operator=(BasicBlockManager const&){};

	public:
		static BasicBlockManager* getManager(){
			if (!_manager)
				_manager = new BasicBlockManager;
			return _manager;
		}

		void resetAllCode(ImmCodes* in=NULL){

			if (in==NULL)
				in = getCode();

			allBasicBlocks.erase(allBasicBlocks.begin(), allBasicBlocks.end());
			vector <BasicBlock*>* basicBlocks = in->divideBasicBlocks();
			vector <BasicBlock*>::iterator it = basicBlocks->begin();
			for(;it!=basicBlocks->end();it++) {
				BasicBlockManager::getManager()->replaceBB((*it)->getLabel(),(*it));
			}
		}



                void iterativeOptimizeBasicBlocks(bool *update){
                	BasicBlock* bb;
                	*update = false;
                	bool update2;
                	vector <BasicBlock*>::iterator it = allBasicBlocks.begin();
                	for(;it!=allBasicBlocks.end();it++) {
                		bb = (*it)->iterativeOptimize(&update2);
                		*update = *update || update2;
						BasicBlockManager::getManager()->replaceBB((*it)->getLabel(),bb);
					}
                }



		BasicBlock* getBlockWithLabel(string inLabel){
			vector <BasicBlock*>::iterator it = allBasicBlocks.begin();
			for(;it!=allBasicBlocks.end();it++) {
				if ((*it)->getLabel() == inLabel)
					return *it;
			}
			//cout << "Block with name "<< inLabel<<" not found, creating one::\n\n";
			BasicBlock* newBB = new BasicBlock(inLabel);
			allBasicBlocks.push_back(newBB);
			return newBB;
		}

		vector <BasicBlock*>* getAllBlocks(){
			return &allBasicBlocks;
		}


		void iterativeDataFlowAnalysis(bool* updated = NULL) {
			vector<BasicBlock*>::iterator it =
				BasicBlockManager::getManager()->getAllBlocks()->begin();
			BasicBlock* bb;
//int count=0; 
			for (; it != BasicBlockManager::getManager()->getAllBlocks()->end(); it++) {
//count+=1;
//if(count==6)
{
				bb = (*it)->iterativeDataFlowAnalysis();
				BasicBlockManager::getManager()->replaceBB((*it)->getLabel(), bb);
}
			}
		}

		void optimizeJumpThread(bool* updated=NULL){
			if (updated)
				*updated = false;
			string labelToRemove = getRemovableBBLabel();

			//cout << "Removable label: "<<labelToRemove<<endl<<	endl;
			string labelToReplace;
			if ( !labelToRemove.empty() && getBlockWithLabel(labelToRemove) &&
					getBlockWithLabel(labelToRemove)->getNextBlocks()->size() == 1){
				labelToReplace = (*(getBlockWithLabel(labelToRemove)->getNextBlocks()))[0];
				//cout << "Replaced by label: "<<labelToReplace<<endl<<	endl;
				//                              vector <BasicBlock*>::iterator it = allBasicBlocks.begin();
				//                              for(;it!=allBasicBlocks.end();it++) {
				replaceBBLabel(labelToRemove, labelToReplace);
				///////replaceBB(labelToRemove);
				vector<string>* lab = new vector<string>();
				lab->push_back(labelToRemove);
				removeLabelNode(lab);

				if (updated) *updated = true;
				//                              }
				//                                      cout << "Replace "<< labelToRemove << " by " << labelToReplace << "\n\n";

			}



		}

		void replaceBBLabel(string oldLabel, string newLabel){
			long newLabelLong = atol(newLabel.substr(1).c_str());
			vector <BasicBlock*>::iterator it = allBasicBlocks.begin();
			for(;it!=allBasicBlocks.end();it++) {
				vector <string>::iterator nextLabel = (*it)->getNextBlocks()->begin();
				for(;nextLabel!=(*it)->getNextBlocks()->end();nextLabel++) {
					if (*nextLabel == oldLabel){

						BasicBlock* oneBlock = new BasicBlock((*it)->getLabel());
						vector <ImmCode*>::iterator code = (*it)->get_code()->begin();

						for(;code!=(*it)->get_code()->end();code++) {
							if (((*code)->getType() != ImmCode::GOTO) && (*code)->getType() != ImmCode::IF_REL && (*code)->getType() != ImmCode::IF)
								oneBlock->add(*code);
							else
								//if ((*it)->getType() == ImmCode::IF_REL || (*it)->getType() == ImmCode::IF)
							{
								void** opnd = (*code)->get_opnd();
								string opnd0 = ((ImmCode*)(opnd[0]))->labelName();
								if (opnd0 == oldLabel)
									oneBlock->add(new ImmCode( (*code)->getType(), (*code)->get_subcode(), (void*)new ImmCode(ImmCode::LABEL, OpNode::INVALID, (void*)newLabelLong), opnd[1], opnd[2]));
								else
									oneBlock->add(*code);
								//oneBlock->addNextBlock(BasicBlockManager::getManager()->getBlockWithLabel(((ImmCode*)(opnd[0]))->labelName()));
								//oneBlock->addNextBlock(((ImmCode*)(opnd[0]))->labelName());
							}

						}

						for(;nextLabel!=(*it)->getNextBlocks()->end();nextLabel++){
							if (*nextLabel == oldLabel)
								oneBlock->addNextBlock(newLabel);
							else
								oneBlock->addNextBlock(*nextLabel);
						}

						replaceBB((*it)->getLabel(), oneBlock);

						//                                              cout << "mmmmmmmmmmmmmmmm\n\n";
						//                                              oneBlock->print(cout);
						//                                              cout << "\n\nmmmmmmmmmmmmmmmm\n\n";

						break;
					}



				}


			}


		}

		string getRemovableBBLabel(){
			vector <BasicBlock*>::iterator it = allBasicBlocks.begin();
			for(;it!=allBasicBlocks.end();it++) {
				if ((*it)->count() == 0){
					//cout << "Block " << (*it)->getLabel() << " has count 0, can be removed?\n";
					return (*it)->getLabel();
				}
				else if ((*it)->count() == 1){
					if ((*((*it)->get_code()))[0]->getType() == ImmCode::GOTO){

						//if ()
						//cout << "Block " << (*it)->getLabel() << " has count 0, can be removed?\n";
						return (*it)->getLabel();
					}
				}
				else{
					//cout << "Block " << (*it)->getLabel() << " has count "<< (*it)->count() <<", cannot be removed?\n";
				}
			}
			return "";
		}

		void replaceBB(string label, BasicBlock* newBB=NULL){
			vector <BasicBlock*>::iterator it = allBasicBlocks.begin();

			for(;it!=allBasicBlocks.end();it++) {
				if ((*it)->getLabel() == label){
					if (newBB)
						for (int i=0; i<(*it)->getNextBlocks()->size(); i++)
							newBB->addNextBlock((*it)->getNextBlocks()->at(i));
					allBasicBlocks.erase(it);
					if (newBB)
						allBasicBlocks.insert(it, newBB);
					//cout << "Replaced!!1\n\n\n";
					break;
				}
			}

		}

		ImmCodes* getCode(){

			ImmCodes* main = new ImmCodes();
			vector <BasicBlock*>::iterator it = allBasicBlocks.begin();
			for(;it!=allBasicBlocks.end();it++) {
				long label = NULL;
				if ((*it)->getLabel().substr(0, 1) == "L"){
					label = atol((*it)->getLabel().substr(1).c_str());
				}
				ImmCode* code= new ImmCode(ImmCode::LABEL,OpNode::INVALID,
						(void*)label,
						(void*)new string((*it)->getLabel()));
				main->add(code);
				main->add(*it);



			}
			return main;
		}


	private: void removeLabelNode(vector<string>* labels);
        public: void removeEmptyBlocks(bool* updated = NULL);
        //public: void removeEmptyLabels(bool* updated = NULL);


};



#endif

